Namespace ShapeLibrary.Design
    '''
    ''' This is an example of very advanced custom
    ''' serialization.  The shape library is designed to work with
    ''' any UI library that uses System.Drawing to draw.  In order to
    ''' easily hook a shape to a library, the shape library contains
    ''' an adapter class for each UI technology. So, for example, 
    ''' the shape library contains a WindowsFormsAdapter object that,
    ''' given a control and a shape container, always draws the
    ''' shape container on the control's surface.  At design time
    ''' we want this to be automatic, so we have written a custom
    ''' code serializer that is associated with all shape containers.
    ''' This serializer looks for a Control property on the shape
    ''' container, and if it is present and set it creates a local
    ''' WindowsFormsAdapter variable that connects up the shape
    ''' container.  The Control property is added by ShapeContainer's
    ''' designer.
    '''
    ''' This is an extreremely
    ''' advanced topic that very few component writers will need
    ''' to use.  Generally code serialization can be adequately
    ''' tweaked by adding a type converter that uses instance
    ''' descriptors as needed.
    '''
    Friend Class ShapeContainerCodeDomSerializer
        Inherits CodeDomSerializer

        '''
        ''' We implement this for the abstract method on CodeDomSerializer.
        ''' We are responsible for reading in a collection of code Dom objects
        ''' and converting these into a ShapeContainer.  All we do here is
        ''' delegate to our base class.
        '''
        Public Overrides Function Deserialize(ByVal manager As IDesignerSerializationManager, ByVal codeObject As Object) As Object
            Return GetBaseSerializer(manager).Deserialize(manager, codeObject)
        End Function

        '''
        ''' This is a small helper method that returns the serializer for ShapeContainer's
        ''' base class (Component).
        '''
        Private Function GetBaseSerializer(ByVal manager As IDesignerSerializationManager) As CodeDomSerializer
            Return CType(manager.GetSerializer(GetType(Component), GetType(CodeDomSerializer)), CodeDomSerializer)
        End Function

        ''' 
        ''' We implement this for the abstract method on CodeDomSerializer.  This method
        ''' takes an object graph, and serializes the object into CodeDom elements.  That's
        ''' a lot of work, but luckily we can delegate to the base serializer for nearly
        ''' all of it.  We add additional support here to create a WindowsFormsAdapter
        ''' to connect a shape container up to a windows form.  We only do this if the
        ''' shape container's designer has offered a "Control" property, indicating that
        ''' it has detected that it has been placed on a Windows Forms object.
        ''' 
        Public Overrides Function Serialize(ByVal manager As IDesignerSerializationManager, ByVal value As Object) As Object
            Dim codeObject As Object = GetBaseSerializer(manager).Serialize(manager, value)

            ' The first step it to see if our object has a Control property that
            ' is properly assigned.
            '
            Dim controlProp As PropertyDescriptor = TypeDescriptor.GetProperties(value)("Control")
            If Not (controlProp Is Nothing) AndAlso controlProp.PropertyType Is GetType(Control) Then
                Dim control As Control = CType(controlProp.GetValue(value), Control)
                If Not (control Is Nothing) Then
                    ' We have a valid control.  Now we must emit a local variable
                    ' declaration for the control and then hook it up.  The
                    ' code we will emit looks like this:
                    '
                    '		WindowsFormsAdapter shape1Adapter = new WindowsFormsAdapter();
                    '		shape1Adapter.Connect(control1, shape1);
                    '
                    Dim adapterName As String = manager.GetName(value) + "Adapter"
                    Dim adapterDecl As CodeVariableDeclarationStatement = New CodeVariableDeclarationStatement(GetType(WindowsFormsAdapter), adapterName)
                    adapterDecl.InitExpression = New CodeObjectCreateExpression(GetType(WindowsFormsAdapter))
                    Dim adapterRef As CodeVariableReferenceExpression = New CodeVariableReferenceExpression(adapterName)

                    Dim connectMethod As CodeMethodInvokeExpression = New CodeMethodInvokeExpression(adapterRef, "Connect")

                    ' SerializeToReferenceExpression is a helper method that is built into CodeDomSerializer.
                    ' It takes any sited object (like this control) and converts it to an appropriate
                    ' expression.
                    '
                    connectMethod.Parameters.Add(SerializeToReferenceExpression(manager, control))
                    connectMethod.Parameters.Add(SerializeToReferenceExpression(manager, value))

                    ' Now we have two statements:  a variable declaration and a method invocation.
                    ' We must add them to the correct statement collection.  But what is the
                    ' "correct" collection?  The answer is that our base serializer probably
                    ' already gave us one.  If it didn't, then we will provide our own.
                    '
                    Dim statements As CodeStatementCollection = Nothing
                    If TypeOf codeObject Is CodeStatementCollection Then
                        statements = CType(codeObject, CodeStatementCollection)
                    Else

                        ' Our base serializer didn't create a bunch of statements.  Instead, it created
                        ' a single statement or expression.  If it created neither, then we must
                        ' bail out and not add this extra code because we don't know how to connect
                        ' it to what our base serializer provided.
                        '
                        If TypeOf codeObject Is CodeExpression Or TypeOf codeObject Is CodeStatement Then
                            statements = New CodeStatementCollection()
                            Dim statement As CodeStatement
                            If TypeOf codeObject Is CodeStatement Then
                                statement = CType(codeObject, CodeStatement)
                            Else
                                statement = New CodeExpressionStatement(CType(codeObject, CodeExpression))
                            End If
                            statements.Add(statement)

                            ' And setup the returning CodeObject to be this new statement collection.
                            '
                            codeObject = statements
                        End If
                    End If

                    If Not (statements Is Nothing) Then
                        statements.Add(adapterDecl)
                        statements.Add(connectMethod)
                    End If
                End If
            End If
            Return codeObject
        End Function
    End Class
End Namespace
